/*
 * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package com.sun.corba.se.impl.oa.poa;

import org.omg.CORBA.INTERNAL;

import com.sun.corba.se.spi.orb.ORB;

import com.sun.corba.se.spi.orbutil.fsm.Action;
import com.sun.corba.se.spi.orbutil.fsm.ActionBase;
import com.sun.corba.se.spi.orbutil.fsm.Guard;
import com.sun.corba.se.spi.orbutil.fsm.GuardBase;
import com.sun.corba.se.spi.orbutil.fsm.State;
import com.sun.corba.se.spi.orbutil.fsm.StateImpl;
import com.sun.corba.se.spi.orbutil.fsm.Input;
import com.sun.corba.se.spi.orbutil.fsm.InputImpl;
import com.sun.corba.se.spi.orbutil.fsm.FSM;
import com.sun.corba.se.spi.orbutil.fsm.FSMImpl;
import com.sun.corba.se.spi.orbutil.fsm.StateEngine;
import com.sun.corba.se.spi.orbutil.fsm.StateEngineFactory;

import com.sun.corba.se.impl.orbutil.concurrent.Mutex;
import com.sun.corba.se.impl.orbutil.concurrent.CondVar;

import org.omg.CORBA.SystemException;

import org.omg.PortableServer.POAPackage.ObjectAlreadyActive;

/**
 * AOMEntry represents a Servant or potential Servant in the ActiveObjectMap.
 * It may be in several states to allow for long incarnate or etherealize operations.
 * The methods on this class mostly represent input symbols to the state machine
 * that controls the lifecycle of the entry.  A library is used to build the state
 * machine rather than the more usual state pattern so that the state machine
 * transitions are explicitly visible.
 */
public class AOMEntry extends FSMImpl {

  private final Thread[] etherealizer;   // The actual etherealize operation
  // for this entry.  It is
  // represented as a Thread because
  // the POA.deactivate_object never
  // waits for the completion.
  private final int[] counter;           // single element holder for counter
  // accessed in actions
  private final CondVar wait;            // accessed in actions

  final POAImpl poa;

  public static final State INVALID = new StateImpl("Invalid");
  public static final State INCARN = new StateImpl("Incarnating") {
    public void postAction(FSM fsm) {
      AOMEntry entry = (AOMEntry) fsm;
      entry.wait.broadcast();
    }
  };
  public static final State VALID = new StateImpl("Valid");
  public static final State ETHP = new StateImpl("EtherealizePending");
  public static final State ETH = new StateImpl("Etherealizing") {
    public void preAction(FSM fsm) {
      AOMEntry entry = (AOMEntry) fsm;
      Thread etherealizer = entry.etherealizer[0];
      if (etherealizer != null) {
        etherealizer.start();
      }
    }

    public void postAction(FSM fsm) {
      AOMEntry entry = (AOMEntry) fsm;
      entry.wait.broadcast();
    }
  };
  public static final State DESTROYED = new StateImpl("Destroyed");

  static final Input START_ETH = new InputImpl("startEtherealize");
  static final Input ETH_DONE = new InputImpl("etherealizeDone");
  static final Input INC_DONE = new InputImpl("incarnateDone");
  static final Input INC_FAIL = new InputImpl("incarnateFailure");
  static final Input ACTIVATE = new InputImpl("activateObject");
  static final Input ENTER = new InputImpl("enter");
  static final Input EXIT = new InputImpl("exit");

  private static Action incrementAction = new ActionBase("increment") {
    public void doIt(FSM fsm, Input in) {
      AOMEntry entry = (AOMEntry) fsm;
      entry.counter[0]++;
    }
  };

  private static Action decrementAction = new ActionBase("decrement") {
    public void doIt(FSM fsm, Input in) {
      AOMEntry entry = (AOMEntry) fsm;
      if (entry.counter[0] > 0) {
        entry.counter[0]--;
      } else {
        throw entry.poa.lifecycleWrapper().aomEntryDecZero();
      }
    }
  };

  private static Action throwIllegalStateExceptionAction = new ActionBase(
      "throwIllegalStateException") {
    public void doIt(FSM fsm, Input in) {
      throw new IllegalStateException(
          "No transitions allowed from the DESTROYED state");
    }
  };

  private static Action oaaAction = new ActionBase("throwObjectAlreadyActive") {
    public void doIt(FSM fsm, Input in) {
      throw new RuntimeException(new ObjectAlreadyActive());
    }
  };

  private static Guard waitGuard = new GuardBase("wait") {
    public Guard.Result evaluate(FSM fsm, Input in) {
      AOMEntry entry = (AOMEntry) fsm;
      try {
        entry.wait.await();
      } catch (InterruptedException exc) {
        // XXX Log this
        // NO-OP
      }

      return Guard.Result.DEFERED;
    }
  };


  private static class CounterGuard extends GuardBase {

    private int value;

    public CounterGuard(int value) {
      super("counter>" + value);
      this.value = value;
    }

    public Guard.Result evaluate(FSM fsm, Input in) {
      AOMEntry entry = (AOMEntry) fsm;
      return Guard.Result.convert(entry.counter[0] > value);
    }
  }

  ;

  private static GuardBase greaterZeroGuard = new CounterGuard(0);
  private static Guard zeroGuard = new Guard.Complement(greaterZeroGuard);
  private static GuardBase greaterOneGuard = new CounterGuard(1);
  private static Guard oneGuard = new Guard.Complement(greaterOneGuard);

  private static StateEngine engine;

  static {
    engine = StateEngineFactory.create();

    //          State,   Input,     Guard,                  Action,             new State

    engine.add(INVALID, ENTER, incrementAction, INCARN);
    engine.add(INVALID, ACTIVATE, null, VALID);
    engine.setDefault(INVALID);

    engine.add(INCARN, ENTER, waitGuard, null, INCARN);
    engine.add(INCARN, EXIT, null, INCARN);
    engine.add(INCARN, START_ETH, waitGuard, null, INCARN);
    engine.add(INCARN, INC_DONE, null, VALID);
    engine.add(INCARN, INC_FAIL, decrementAction, INVALID);
    engine.add(INCARN, ACTIVATE, oaaAction, INCARN);

    engine.add(VALID, ENTER, incrementAction, VALID);
    engine.add(VALID, EXIT, decrementAction, VALID);
    engine.add(VALID, START_ETH, greaterZeroGuard, null, ETHP);
    engine.add(VALID, START_ETH, zeroGuard, null, ETH);
    engine.add(VALID, ACTIVATE, oaaAction, VALID);

    engine.add(ETHP, ENTER, waitGuard, null, ETHP);
    engine.add(ETHP, START_ETH, null, ETHP);
    engine.add(ETHP, EXIT, greaterOneGuard, decrementAction, ETHP);
    engine.add(ETHP, EXIT, oneGuard, decrementAction, ETH);
    engine.add(ETHP, ACTIVATE, oaaAction, ETHP);

    engine.add(ETH, START_ETH, null, ETH);
    engine.add(ETH, ETH_DONE, null, DESTROYED);
    engine.add(ETH, ACTIVATE, oaaAction, ETH);
    engine.add(ETH, ENTER, waitGuard, null, ETH);

    engine.setDefault(DESTROYED, throwIllegalStateExceptionAction, DESTROYED);

    engine.done();
  }

  public AOMEntry(POAImpl poa) {
    super(engine, INVALID, ((ORB) poa.getORB()).poaFSMDebugFlag);
    this.poa = poa;
    etherealizer = new Thread[1];
    etherealizer[0] = null;
    counter = new int[1];
    counter[0] = 0;
    wait = new CondVar(poa.poaMutex,
        ((ORB) poa.getORB()).poaConcurrencyDebugFlag);
  }

  // Methods that drive the FSM: the real interface to this class
  // Most just call the doIt method, but startEtherealize needs
  // the etherealizer.
  public void startEtherealize(Thread etherealizer) {
    this.etherealizer[0] = etherealizer;
    doIt(START_ETH);
  }

  public void etherealizeComplete() {
    doIt(ETH_DONE);
  }

  public void incarnateComplete() {
    doIt(INC_DONE);
  }

  public void incarnateFailure() {
    doIt(INC_FAIL);
  }

  public void activateObject() throws ObjectAlreadyActive {
    try {
      doIt(ACTIVATE);
    } catch (RuntimeException exc) {
      Throwable thr = exc.getCause();
      if (thr instanceof ObjectAlreadyActive) {
        throw (ObjectAlreadyActive) thr;
      } else {
        throw exc;
      }
    }
  }

  public void enter() {
    doIt(ENTER);
  }

  public void exit() {
    doIt(EXIT);
  }
}
